/*
 * Copyright (c) 2009 Charles S. Wilson
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a 
 * copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the 
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included 
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
 * OTHER DEALINGS IN THE SOFTWARE.
 */
#include <stdio.h>
#include <errno.h>
#include <malloc.h>
#include <string.h>
#include "confdata.h"
#include "util.h"

run2_selfoptions_t *
run2_selfoptions_create (void)
{
  run2_selfoptions_t * v = (run2_selfoptions_t *)
    run2_malloc (sizeof (run2_selfoptions_t));
  v->args = NULL;
  return v;
}

void
run2_selfoptions_delete (run2_selfoptions_t *arg)
{
  if (arg)
    {
      if (arg->args)
        {
          cdsl_dlist_destroy_list (arg->args, &free);
          arg->args = NULL;
        }
      free (arg);
    }
}

#define INDENT 2
static Ustr *
indent_spacing (int level)
{
  int i = 0;
  Ustr * s = USTR_CHECK (ustr_dup_empty ());
  ustr_add_rep_chr (&s, ' ', (size_t)(level * INDENT));
  return s;
}

static void
run2_args_print (FILE *f, int level, cdsl_dlist_t *list)
{
  if (list && cdsl_dlist_size (list) > 0)
    {
      int i = 0;
      Ustr * s = indent_spacing (level);
      Ustr * l = USTR_CHECK (ustr_dup_empty ());
      cdsl_dlist_node_t *p;
      for (p = cdsl_dlist_begin (list);
           p != cdsl_dlist_end (list);
           p = cdsl_dlist_next (p))
        {
          /* effectively: fprintf (f, "%sargv[%d]: '%s'\n", ustr_cstr (s), i++,
           *                       (char*)cdsl_dlist_value (p));
           * only more efficient.
           */
          ustr_set_empty (&l);
          ustr_add_fmt (&l, "%sargv[%d]: '%s'\n", ustr_cstr (s), i++,
                        ustr_cstr ((Ustr*)cdsl_dlist_value (p)));
          USTR_CHECK (l);
          ustr_io_putfile (&l, f);
        }
      ustr_free (l);
      ustr_free (s);
    }
}

void
run2_selfoptions_print (FILE * f, int level, const Ustr *name,
                        const run2_selfoptions_t *elem)
{
  int i = 1;
  Ustr * s = indent_spacing (level);
  ustr_add (&s, name);
  USTR_ADD_OSTR (&s, ":\n");
  USTR_CHECK (s);
  ustr_io_putfile (&s, f);
  ustr_free (s);
  run2_args_print(f, level + 1, elem->args);
}

run2_env_spec_t *
run2_env_spec_create (void)
{
  run2_env_spec_t * v = (run2_env_spec_t *)
    run2_malloc (sizeof (run2_env_spec_t));
  v->set = NULL;
  v->prepend = NULL;
  v->append = NULL;
  return v;
}

run2_env_spec_t *
run2_env_spec_copy (run2_env_spec_t * arg)
{
  run2_env_spec_t * v = run2_env_spec_create ();
  v->set = NULL;
  v->prepend = NULL;
  v->append = NULL;
  if (arg)
  {
    if (arg->set)
      v->set = USTR_CHECK (ustr_dup_buf (ustr_cstr (arg->set),
                                         ustr_len (arg->set)));
    if (arg->prepend && cdsl_dlist_size (arg->prepend) > 0)
      {
        int i = 0;
        cdsl_dlist_node_t *p;
        for (p = cdsl_dlist_begin (arg->prepend);
             p != cdsl_dlist_end (arg->prepend);
             p = cdsl_dlist_next (p))
          {
            run2_env_spec_prepend (v, (Ustr*)cdsl_dlist_value (p));
          }
      }
    if (arg->append && cdsl_dlist_size (arg->append) > 0)
      {
        int i = 0;
        cdsl_dlist_node_t *p;
        for (p = cdsl_dlist_begin (arg->append);
             p != cdsl_dlist_end (arg->append);
             p = cdsl_dlist_next (p))
          {
            run2_env_spec_append (v, (Ustr*)cdsl_dlist_value (p));
          }
      }
  }
  return v;
}

static void *
run2_env_spec_copy_wrap (void *arg)
{
   return (run2_env_spec_t *) run2_env_spec_copy ((run2_env_spec_t *)arg);
}

void
run2_env_spec_delete (run2_env_spec_t *arg)
{
  if (arg)
    {
      if (arg->set)
        {
          ustr_free (arg->set);
          arg->set = NULL;
        }
      if (arg->prepend)
        {
          cdsl_dlist_destroy_list (arg->prepend, &free);
          arg->prepend = NULL;
        }
      if (arg->append)
        {
          cdsl_dlist_destroy_list (arg->append, &free);
          arg->append = NULL;
        }
      free (arg);
    }
}

static void
run2_env_spec_delete_wrap (void *arg)
{
  run2_env_spec_delete ((run2_env_spec_t *)arg);
}

void
run2_env_spec_print  (FILE * f, int level, const Ustr *name,
                      const run2_env_spec_t *elem)
{
  Ustr * s = indent_spacing (level);
  if (elem->set)
    {
      /* effectively:
       *    fprintf (f, "%s%s[set]: '%s'\n", s, name, elem->set)
       * only more efficient.
       */
      Ustr * l = USTR_CHECK (ustr_dup_buf (ustr_cstr (s),
                                           ustr_len (s)));
      ustr_add (&l, name);
      USTR_ADD_OSTR (&l, "[set]: '");
      ustr_add (&l, elem->set);
      USTR_ADD_OSTR (&l, "'\n");
      USTR_CHECK (l);
      ustr_io_putfile (&l, f);
      ustr_free (l);
    }
  if (elem->prepend && cdsl_dlist_size (elem->prepend) > 0)
    {
      int i = 0;
      cdsl_dlist_node_t *p;
      Ustr * l = USTR_CHECK (ustr_dup_empty ());
      for (p = cdsl_dlist_begin (elem->prepend);
           p != cdsl_dlist_end (elem->prepend);
           p = cdsl_dlist_next (p))
        {
          /* effectively: fprintf (f, "%s%s[pre]: '%s'\n", s, name,
           *                       (char*)cdsl_dlist_value (p));
           * only more efficient.
           */
          ustr_set_empty (&l);
          ustr_add (&l, s);
          ustr_add (&l, name);
          USTR_ADD_OSTR (&l, "[pre]: '");
          ustr_add (&l, (Ustr*)cdsl_dlist_value (p));
          USTR_ADD_OSTR (&l, "'\n");
          USTR_CHECK (l);
          ustr_io_putfile (&l, f);
        }
      ustr_free (l);
    }
  if (elem->append && cdsl_dlist_size (elem->append) > 0)
    {
      int i = 0;
      cdsl_dlist_node_t *p;
      Ustr * l = USTR_CHECK (ustr_dup_empty ());
      for (p = cdsl_dlist_begin (elem->append);
           p != cdsl_dlist_end (elem->append);
           p = cdsl_dlist_next (p))
        {
          /* effectively: fprintf (f, "%s%s[app]: '%s'\n", s, name,
           *                       (char*)cdsl_dlist_value (p));
           * only more efficient.
           */
          ustr_set_empty (&l);
          ustr_add (&l, s);
          ustr_add (&l, name);
          USTR_ADD_OSTR (&l, "[app]: '");
          ustr_add (&l, (Ustr*)cdsl_dlist_value (p));
          USTR_ADD_OSTR (&l, "'\n");
          USTR_CHECK (l);
          ustr_io_putfile (&l, f);
        }
    }
}


run2_env_t *
run2_env_create (void)
{
  run2_env_t * v = (run2_env_t *)
    run2_malloc (sizeof (run2_env_t));
  v->map = NULL;
  return v;
}

void
run2_env_delete (run2_env_t *arg)
{
  if (arg)
    {
      if (arg->map)
	{
	  cdsl_map_delete (arg->map);
	  arg->map = NULL;
	}
      free (arg);
    }
}

void
run2_env_print  (FILE * f, int level, const Ustr *name,
                 const run2_env_t *elem)
{
  /* fprintf (f, "%s%s:\n", s, name); */
  Ustr * s = indent_spacing (level);
  ustr_add (&s, name);
  USTR_ADD_OSTR (&s, ":\n");
  USTR_CHECK (s);
  ustr_io_putfile (&s, f);
  ustr_free (s);

  if (elem->map && !cdsl_map_empty (elem->map))
    {
      cdsl_map_iter_t * iter = cdsl_map_iter_new (elem->map);
      if (!iter)
        {
          errorMsg ("(%s[%d]): %s", __func__, __LINE__, strerror(errno));
          exit (1);
        }
      cdsl_map_first (elem->map, iter);
      while (!cdsl_map_iter_end (iter))
        {
          const Ustr *var = (const Ustr *)cdsl_map_key (iter);
          const run2_env_spec_t *spec = (const run2_env_spec_t *)
            cdsl_map_value (iter);
          run2_env_spec_print (f, level+1, var, spec);
          cdsl_map_next (iter);
        }
      cdsl_map_iter_delete (iter);
    }
}

run2_global_t *
run2_global_create (void)
{
  run2_global_t * v = (run2_global_t *)
    run2_malloc (sizeof (run2_global_t));
  v->env = NULL;
  v->tgt = NULL;
  return v;
}

void
run2_global_delete (run2_global_t *arg)
{
  if (arg)
    {
      if (arg->env)
	{
          run2_env_delete (arg->env);
	  arg->env = NULL;
	}
      free (arg);
    }
}

void run2_global_print (FILE * f, int level, const Ustr *name,
                        const run2_global_t *elem)
{
  /* fprintf (f, "%s%s:\n", s, name); */
  Ustr * s = indent_spacing (level);
  ustr_add (&s, name);
  USTR_ADD_OSTR (&s, ":\n");
  USTR_CHECK (s);
  ustr_io_putfile (&s, f);
  ustr_free (s);

  if (elem->env)
    {
      run2_env_print (f, level+1, USTR1 (\xB, "Environment"), elem->env);
    }
  if (elem->tgt)
    {
      run2_tgt_spec_print (f, level+1, USTR1 (\6, "Target"), elem->tgt);
    }
}

run2_tgt_spec_t *
run2_tgt_spec_create (void)
{
  run2_tgt_spec_t * v = (run2_tgt_spec_t *)
    run2_malloc (sizeof (run2_tgt_spec_t));
  v->filename = NULL;
  v->startin  = NULL;
  v->args     = NULL;
  return v;
}

void
run2_tgt_spec_delete (run2_tgt_spec_t *arg)
{
  if (arg)
    {
      if (arg->filename)
	{
          ustr_free (arg->filename);
	  arg->filename = NULL;
	}
      if (arg->startin)
	{
          ustr_free (arg->startin);
	  arg->startin = NULL;
	}
      if (arg->args)
	{
          cdsl_dlist_destroy_list (arg->args, &free);
	  arg->args = NULL;
	}
      free (arg);
    }
}

void
run2_tgt_spec_print (FILE * f, int level, const Ustr *name,
                     const run2_tgt_spec_t *elem)
{
  Ustr * l;
  /* fprintf (f, "%s%s:\n", s, name); */
  Ustr * s = indent_spacing (level);
  ustr_add (&s, name);
  USTR_ADD_OSTR (&s, ":\n");
  USTR_CHECK (s);
  ustr_io_putfile (&s, f);
  ustr_free (s);

  s = indent_spacing (level + 1);
  l = USTR_CHECK (ustr_dup_empty ());
  if (elem->filename && ustr_len (elem->filename) > 0)
    {
      /* fprintf (f, "%sfilename: '%s'\n", s, elem->filename); */
      ustr_set_empty (&l);
      ustr_add (&l, s);
      USTR_ADD_OSTR (&l, "filename: '");
      ustr_add (&l, elem->filename);
      USTR_ADD_OSTR (&l, "'\n");
      USTR_CHECK (l);
      ustr_io_putfile (&l, f);
    }
  if (elem->startin) /* empty is ok */
    {
      /* fprintf (f, "%sstartin: '%s'\n", s, elem->startin); */
      ustr_set_empty (&l);
      ustr_add (&l, s);
      USTR_ADD_OSTR (&l, "startin: '");
      ustr_add (&l, elem->startin);
      USTR_ADD_OSTR (&l, "'\n");
      USTR_CHECK (l);
      ustr_io_putfile (&l, f);
    }
  ustr_free (s);
  ustr_free (l);
  run2_args_print(f, level + 1, elem->args);
}

run2_tgtopts_t *
run2_tgtopts_create (void)
{
  run2_tgtopts_t * v = (run2_tgtopts_t *)
    run2_malloc (sizeof (run2_tgtopts_t));
  v->env = NULL;
  v->tgt = NULL;
  return v;
}

void
run2_tgtopts_delete (run2_tgtopts_t *arg)
{
  if (arg)
    {
      if (arg->env)
	{
          run2_env_delete (arg->env);
	  arg->env = NULL;
	}
      if (arg->tgt)
	{
          run2_tgt_spec_delete (arg->tgt);
	  arg->tgt = NULL;
	}
      free (arg);
    }
}

void run2_tgtopts_print (FILE * f, int level, const Ustr *name,
                         const run2_tgtopts_t *elem)
{
  /* fprintf (f, "%s%s:\n", s, name); */
  Ustr * s = indent_spacing (level);
  ustr_add (&s, name);
  USTR_ADD_OSTR (&s, ":\n");
  USTR_CHECK (s);
  ustr_io_putfile (&s, f);
  ustr_free (s);

  if (elem->env)
    {
      run2_env_print (f, level+1, USTR1 (\xB, "Environment"), elem->env);
    }
  if (elem->tgt)
    {
      run2_tgt_spec_print (f, level+1, USTR1 (\6, "Target"), elem->tgt);
    }
}

run2_confdata_t *
run2_confdata_create (void)
{
  run2_confdata_t * v = (run2_confdata_t *)
    run2_malloc (sizeof (run2_confdata_t));
  v->selfopt = NULL;
  v->global  = NULL;
  v->gdi     = NULL;
  v->x11     = NULL;
  return v;
}

void
run2_confdata_delete (run2_confdata_t *arg)
{
  if (arg)
    {
      if (arg->selfopt)
	{
          run2_selfoptions_delete (arg->selfopt);
	  arg->selfopt = NULL;
	}
      if (arg->global)
	{
          run2_global_delete (arg->global);
	  arg->global= NULL;
	}
      if (arg->gdi)
	{
          run2_tgtopts_delete (arg->gdi);
	  arg->gdi= NULL;
	}
      if (arg->x11)
	{
          run2_tgtopts_delete (arg->x11);
	  arg->x11= NULL;
	}
      free (arg);
    }
}

void
run2_confdata_print (FILE * f, int level, const Ustr *name,
                     const run2_confdata_t *elem)
{
  /* fprintf (f, "%s%s:\n", s, name); */
  Ustr * s = indent_spacing (level);
  ustr_add (&s, name);
  USTR_ADD_OSTR (&s, ":\n");
  USTR_CHECK (s);
  ustr_io_putfile (&s, f);
  ustr_free (s);

  if (elem->selfopt)
    {
      run2_selfoptions_print (f, level+1, USTR1 (\xB, "SelfOptions"), elem->selfopt);
    }
  if (elem->global)
    {
      run2_global_print (f, level+1, USTR1 (\6, "Global"), elem->global);
    }
  if (elem->gdi)
    {
      run2_tgtopts_print (f, level+1, USTR1 (\3, "GDI"), elem->gdi);
    }
  if (elem->x11)
    {
      run2_tgtopts_print (f, level+1, USTR1 (\3, "X11"), elem->x11);
    }
}

static int
hasNonWS (const Ustr *s)
{
  size_t len = ustr_len (s);
  int rv = 0;
  int i;
  const char *p = ustr_cstr (s);
  for (i = 0; i < len && rv == 0; i++)
    if (!isspace ((int)(*p++)))
      rv = 1;
  return rv;
}

static int
run2_add_arg (const Ustr    *caller,
              cdsl_dlist_t **list,
              const Ustr    *entry)
{
  if (!*list)
    {
      *list = cdsl_dlist_new_list ();
      if (!*list)
        {
          errorMsg ("(%s[%d] [%s]): %s", __func__, __LINE__,
                    caller, strerror(errno));
          exit (1);
        }
    }
  Ustr *data = USTR_CHECK (ustr_dup_buf (ustr_cstr (entry), ustr_len (entry)));
  cdsl_dlist_node_t *n = cdsl_dlist_insert_after (
    *list, cdsl_dlist_end (*list), (void *)data);
  if (!n)
    {
      errorMsg ("(%s[%d] [%s]): unable to add %s to args",
                __func__, __LINE__, ustr_cstr (caller), ustr_cstr (entry));
      exit (1);
    }
  return 0;
}

int
run2_selfoptions_add_arg (run2_selfoptions_t *elem,
                          const Ustr         *entry)
{
  int rv = 0;
  if (entry && ustr_len (entry) > 0 && hasNonWS (entry))
  {
    rv = run2_add_arg (USTR1 (\x18, "run2_selfoptions_add_arg"), &(elem->args), entry);
  }
  return rv;
}

int run2_env_spec_set     (run2_env_spec_t *elem,
                           const Ustr      *set)
{
  if (set) /* empty string is ok; means "unset" */
    {
      if (elem->set)
        {
          ustr_set_empty (&elem->set);
          ustr_add (&elem->set, set);
          USTR_CHECK (elem->set);
        }
      else
        {
          elem->set = USTR_CHECK (ustr_dup_buf (ustr_cstr (set), ustr_len (set)));
        }
    }
  return 0;
}

int
run2_env_spec_prepend (run2_env_spec_t *elem,
                       const Ustr      *prepend)
{
  int rv = 0;
  if (prepend && ustr_len (prepend) > 0 && hasNonWS (prepend))
  {
    /* STUB: tokenize */
    rv = run2_add_arg (USTR1 (\x15, "run2_env_spec_prepend"), &(elem->prepend), prepend);
  }
  return rv;
}

int
run2_env_spec_append  (run2_env_spec_t *elem,
                       const Ustr      *append)
{
  int rv = 0;
  if (append && ustr_len (append) > 0 && hasNonWS (append))
  {
    /* STUB: tokenize */
    rv = run2_add_arg (USTR1 (\x14, "run2_env_spec_append"), &(elem->append), append);
  }
  return rv;
}

static int
run2_ustricmp_wrap(void *a, void *b)
{
  const Ustr * ua = (const Ustr *)a;
  const Ustr * ub = (const Ustr *)b;
  if (!ua && !ub) return 0;
  if (!ua) return -1;
  if (!ub) return 1;
  return ustr_cmp_case (ua, ub);
}
static void *
run2_ustr_dup_wrap(void *a)
{
  const Ustr * ua = (const Ustr *)a;
  return (void *) USTR_CHECK (ustr_dup_buf (ustr_cstr (a), ustr_len (a)));
}
static void
run2_ustr_free_wrap (void *a)
{
  Ustr * ua = (Ustr *)a;
  ustr_free (ua);
}

static int
validVarName(const Ustr *s)
{
  int rv = 1;
  int i;
  const char *p = ustr_cstr (s);
  size_t len = strlen (p);
  for (i = 0; i < len && rv == 1; i++)
    {
      if (!isalnum ((int)*p) && *p != '_')
        rv = 0;
      ++p;
    }
  return rv;
}

run2_env_spec_t *
run2_env_get_var (run2_env_t *elem,
                  const Ustr *varname)
{
  run2_env_spec_t *envspec;
  cdsl_map_iter_t *iter;

  if (!varname || ustr_len(varname) == 0 || !validVarName(varname))
    {
      errorMsg ("(%s[%d]): bad variable name `%s'",
                __func__, __LINE__, ustr_cstr (varname));
      exit (1);
    }
  if (!elem->map)
    {
      elem->map = cdsl_map_new (&run2_ustricmp_wrap,
                                &run2_ustr_dup_wrap,
                                &run2_ustr_free_wrap,
                                &run2_env_spec_copy_wrap,
                                &run2_env_spec_delete_wrap);
      if (!elem->map)
        {
          errorMsg ("(%s[%d]): %s", __func__,
                    __LINE__, strerror(errno));
          exit (1);
        }
    }

  /* first see if it is already present */
  iter = cdsl_map_iter_new (elem->map);
  if (!iter)
    {
      errorMsg ("(%s[%d]): %s", __func__, __LINE__, strerror(errno));
      exit (1);
    }
  if (cdsl_map_find (elem->map, (void *)varname, iter))
    {
      /* it is already present; return it */
      envspec = cdsl_map_value (iter);
    }
  else
    {
      Ustr * varname_copy = USTR_CHECK (ustr_dup_buf (ustr_cstr (varname),
                                                      ustr_len (varname)));
      envspec = run2_env_spec_create ();
      if (!cdsl_map_insert (elem->map, (void *)varname_copy, (void *)envspec))
        {
          errorMsg ("(%s[%d]): unable to add entry for variable `%s'",
                    __func__, __LINE__, ustr_cstr (varname));
          exit (1);
        }
      /* insert makes a copy, so clean up original and get pointer to copy */
      run2_env_spec_delete (envspec);
      if (!cdsl_map_find (elem->map, (void *)varname, iter))
      {
         errorMsg ("(%s[%d]): unable to retrieve just-added entry for variable `%s'",
                    __func__, __LINE__, ustr_cstr (varname));
         exit (1);
      }
      envspec = cdsl_map_value (iter);
    }
  cdsl_map_iter_delete (iter);
  return envspec;
}

run2_env_t *
run2_global_get_env (run2_global_t *elem)
{
  if (!elem->env)
    {
      elem->env = run2_env_create ();
      if (!elem->env)
        {
          errorMsg ("(%s[%d]): %s", __func__, __LINE__, strerror(errno));
          exit (1);
        }
    }
  return elem->env;
}

run2_tgt_spec_t *
run2_global_get_tgt (run2_global_t *elem)
{
  if (!elem->tgt)
    {
      elem->tgt = run2_tgt_spec_create ();
      if (!elem->tgt)
        {
          errorMsg ("(%s[%d]): %s", __func__, __LINE__, strerror(errno));
          exit (1);
        }
    }
  return elem->tgt;
}

int
run2_tgt_spec_add_arg (run2_tgt_spec_t *elem,
                       const Ustr      *entry)
{
  int rv = 0;
  if (entry && ustr_len (entry) > 0 && hasNonWS (entry))
  {
    /* STUB: tokenize */
    rv = run2_add_arg (USTR1 (\x15, "run2_tgt_spec_add_arg"), &(elem->args), entry);
  }
  return rv;
}

int
run2_tgt_spec_set_filename (run2_tgt_spec_t *elem,
                            const Ustr      *filename)
{
  if (filename && ustr_len (filename) > 0)
    {
      if (elem->filename)
        {
          ustr_set_empty (&elem->filename);
          ustr_add (&elem->filename, filename);
          USTR_CHECK (elem->filename);
        }
      else
        {
          elem->filename = USTR_CHECK (ustr_dup_buf (ustr_cstr (filename), ustr_len (filename)));
        }
    }
  return 0;
}

int
run2_tgt_spec_set_startin (run2_tgt_spec_t *elem,
                           const Ustr      *startin)
{
  if (startin) /* empty string is ok; means "unset" */
    {
      if (elem->startin)
        {
          ustr_set_empty (&elem->startin);
          ustr_add (&elem->startin, startin);
          USTR_CHECK (elem->startin);
        }
      else
        {
          elem->startin = USTR_CHECK (ustr_dup_buf (ustr_cstr (startin), ustr_len (startin)));
        }
    }
  return 0;
}

run2_env_t *
run2_tgtopts_get_env (run2_tgtopts_t *elem)
{
  if (!elem->env)
    {
      elem->env = run2_env_create ();
      if (!elem->env)
        {
          errorMsg ("(%s[%d]): %s", __func__, __LINE__, strerror(errno));
          exit (1);
        }
    }
  return elem->env;
}

run2_tgt_spec_t *
run2_tgtopts_get_tgt (run2_tgtopts_t *elem)
{
  if (!elem->tgt)
    {
      elem->tgt = run2_tgt_spec_create ();
      if (!elem->tgt)
        {
          errorMsg ("(%s[%d): %s", __func__, __LINE__, strerror(errno));
          exit (1);
        }
    }
  return elem->tgt;
}

run2_selfoptions_t *
run2_confdata_get_selfoptions (run2_confdata_t *elem)
{
  if (!elem->selfopt)
    {
      elem->selfopt = run2_selfoptions_create ();
      if (!elem->selfopt)
        {
          errorMsg ("(%s[%d]): %s", __func__, __LINE__,
                    strerror(errno));
          exit (1);
        }
    }
  return elem->selfopt;
}

run2_global_t *
run2_confdata_get_global (run2_confdata_t *elem)
{
  if (!elem->global)
    {
      elem->global = run2_global_create ();
      if (!elem->global)
        {
          errorMsg ("(%s[%d]): %s", __func__, __LINE__,
                    strerror(errno));
          exit (1);
        }
    }
  return elem->global;
}

run2_tgtopts_t *
run2_confdata_get_gdi (run2_confdata_t *elem)
{
  if (!elem->gdi)
    {
      elem->gdi = run2_tgtopts_create ();
      if (!elem->gdi)
        {
          errorMsg ("(%s[%d]): %s", __func__, __LINE__,
                    strerror(errno));
          exit (1);
        }
    }
  return elem->gdi;
}

run2_tgtopts_t *
run2_confdata_get_x11 (run2_confdata_t *elem)
{
  if (!elem->x11)
    {
      elem->x11 = run2_tgtopts_create ();
      if (!elem->x11)
        {
          errorMsg ("(%s[%d]): %s", __func__,  __LINE__,
                    strerror(errno));
          exit (1);
        }
    }
  return elem->x11;
}
